package com.ssb.admin.modules;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.ssb.admin.common.annotation.OptLog;
import com.ssb.admin.common.exception.ApiException;
import com.ssb.admin.common.utils.IpUtils;
import com.ssb.admin.common.utils.JwtUtils;
import com.ssb.admin.common.utils.R;
import com.ssb.admin.common.utils.ShiroUtils;
import com.ssb.admin.constant.OptLogConstant;
import com.ssb.admin.constant.ResponseStatus;
import com.ssb.admin.modules.sys.role.entity.SysRole;
import com.ssb.admin.modules.sys.role.entity.SysUserRole;
import com.ssb.admin.modules.sys.role.service.ISysRoleService;
import com.ssb.admin.modules.sys.role.service.ISysUserRoleService;
import com.ssb.admin.modules.sys.user.entity.SysUser;
import com.ssb.admin.modules.sys.user.service.ISysUserService;
import io.netty.util.internal.StringUtil;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;

/**
 * <p>
 * 系统登录
 * </p>
 *
 * @author duaichao
 * @since 2018-08-23
 */
@Slf4j
@Api(value = "系统登录", tags = "系统登录")
@RestController
public class LoginController {
    private final ISysUserService sysUserService;
    private final ISysUserRoleService sysUserRoleService;
    private final ISysRoleService sysRoleService;

    @Autowired
    public LoginController(ISysUserService sysUserService,
                           ISysUserRoleService sysUserRoleService,
                           ISysRoleService sysRoleService
    ) {
        this.sysUserService = sysUserService;
        this.sysUserRoleService = sysUserRoleService;
        this.sysRoleService = sysRoleService;
    }
    @Autowired
    DefaultKaptcha defaultKaptcha;

    @ApiOperation(value="用户登录", notes = "登录认证")
    @ApiImplicitParam(name="sysUser", value="用户对象实例", required = true, dataType = "SysUser")
    @ApiResponses(value = {
            @ApiResponse(code = 20000, message = "成功，返回Token"),
            @ApiResponse(code = 50001, message = "用户名/密码错误"),
            @ApiResponse(code = 50003, message = "登录失败5次，账户锁定10分钟"),
            @ApiResponse(code = 50004, message = "认证失败"),
            @ApiResponse(code = 50005, message = "验证码错误"),
            @ApiResponse(code = 50006, message = "账号已锁定，联系管理员解锁")
    })
    @PostMapping("/login/login")
    @OptLog(OptLogConstant.LOGIN)
    public R login(@RequestBody SysUser sysUser, HttpServletRequest request) {
        ResponseStatus statusCode = null;
        String loginName = sysUser.getLoginName();
        // Redis账户是否锁定
        if(ShiroUtils.isLockedUser(loginName)){
            return R.error(ResponseStatus.FAIL_LOGIN_LOCK_TEMP);
        }


        Subject subject = SecurityUtils.getSubject();
        String trueCode = ShiroUtils.getKaptchaCode();
        log.info("###【当前登陆信息】登陆名/密码："+loginName+"=="+sysUser.getPassword());
        // 验证码
        if (!StringUtil
                .isNullOrEmpty(sysUser.getCaptcha())
                &&
                !sysUser.getCaptcha()
                        .toLowerCase()
                        .equalsIgnoreCase(trueCode)) {
            return R.error(ResponseStatus.FAIL_LOGIN_CAPTCHA_INPUT);
        }
        UsernamePasswordToken token = new UsernamePasswordToken(loginName, sysUser.getPassword());
        if(sysUser.getRememberMe()!=null&&sysUser.getRememberMe()){
            token.setRememberMe(true);
        }

        try {
            //登录，即身份验证
            subject.login(token);
            if (subject.isAuthenticated()) {
                // 获取用户
               Wrapper<SysUser> ew = new EntityWrapper<SysUser>()
                        .eq("login_name", loginName);
                sysUser = sysUserService.selectOne(ew);
                Wrapper<SysUserRole> ewr = new EntityWrapper<SysUserRole>().eq("user_id", sysUser.getId());
                SysUserRole sysUserRoleEntity = sysUserRoleService.selectOne(ewr);
                SysRole sysRole = sysRoleService.selectById(sysUserRoleEntity.getRoleId());
                sysUser.setRole(sysRole);

                sysUser.setIp(IpUtils.getIpAddr(request));
                ShiroUtils.setUserInfo(sysUser);
            }
        }  catch (UnknownAccountException e){
            statusCode = ResponseStatus.FAIL_LOGIN_ERR;
        } catch (LockedAccountException e){
            statusCode = ResponseStatus.FAIL_LOGIN_LOCKED;
        } catch (ExcessiveAttemptsException e) {
            // Redis锁定用户10分钟 如果未开启Redis 数据库永久锁定
            if(!ShiroUtils.lockUser(sysUser.getLoginName())){
                sysUser.setStatus(1);
                sysUserService.updateByLoginName(sysUser);
            }
            statusCode = ResponseStatus.FAIL_LOGIN_LOCK_TEMP;
        } catch (AuthenticationException e) {
            statusCode = ResponseStatus.FAIL_LOGIN_AUTH;
        }
        if(statusCode != null){
            token.clear();
            return R.error(statusCode);
        }else{
            // 返回认证成功jwt token
            String jwtToken = JwtUtils.buildJWT(sysUser.getId().toString());
            log.info("登录成功返回token:"+jwtToken);
            return R.ok("登录成功").data("token",jwtToken);
        }
    }

    @ApiOperation(value="退出登录", notes = "退出登录")
    @PostMapping("/login/logout")
    @OptLog(value = OptLogConstant.LOGOUT, isAfter = false, isBefore = true)
    public R logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return R.ok("成功退出");
    }

    @GetMapping("/login/kaptcha")
    @ApiOperation(value="验证码", notes = "验证码")
    public void kaptcha(HttpServletResponse response){
        byte[] captchaChallengeAsJpeg;
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        try {
            String codeText = defaultKaptcha.createText();
            ShiroUtils.setKaptchaCode(codeText.toLowerCase());
            BufferedImage challenge = defaultKaptcha.createImage(codeText);
            ImageIO.write(challenge, "jpg", jpegOutputStream);

            captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
            response.setHeader("Cache-Control", "no-store");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setContentType("image/jpeg");
            ServletOutputStream responseOutputStream = response.getOutputStream();
            responseOutputStream.write(captchaChallengeAsJpeg);
            responseOutputStream.flush();
            responseOutputStream.close();
        }catch (Exception e){
            log.error("获取验证码错误",e);
            throw new ApiException("获取验证码错误");
        }
    }

    @ApiIgnore()
    @GetMapping("/login.do")
    public R dologin() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return R.ok();
    }
    @ApiIgnore()
    @GetMapping("/403.do")
    public R do403() {
        return R.error("没有权限");
    }
}
